home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / cisco / tacacsd.shar / xtacacsd / tacpasswd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-07  |  18.7 KB  |  687 lines

  1. /*
  2.  * tacpasswd
  3.  *
  4.  * This program maintains the /etc/tacpasswd file.  It can be used to
  5.  * generate or change passwords for the TACACS system.
  6.  *
  7.  * The password generator routines are from Brandon Allbery, as
  8.  * distribured in  comp.sources.misc, volume 5, pwgen.
  9.  *
  10.  * Written by: David Hampton, 30 June 89
  11.  */
  12.  
  13. #ifdef SYSV
  14. #define random rand
  15. #define srandom srand
  16. #endif
  17.  
  18. /*
  19. Path: xanth!mcnc!gatech!cwjcc!hal!ncoast!allbery
  20. From: allbery@ncoast.UUCP (Brandon S. Allbery)
  21. Newsgroups: comp.sources.misc
  22. Subject: v05i059: pwgen -- random-but-pronounceable password generator
  23. Message-ID: <13184@ncoast.UUCP>
  24. Date: 26 Nov 88 04:44:18 GMT
  25. Sender: allbery@ncoast.UUCP
  26. Reply-To: allbery@ncoast.UUCP (Brandon S. Allbery)
  27. Organization: Cleveland Public Access UN*X, Cleveland, Oh
  28. Lines: 328
  29. Approved: allbery@ncoast.UUCP
  30.  
  31. Posting-number: Volume 5, Issue 59
  32. Submitted-by: "Brandon S. Allbery" <allbery@ncoast.UUCP>
  33. Archive-name: pwgen
  34.  
  35. [Dr. Jekyll and Mr. Hyde, anyone?  ;-)  ++bsa]
  36.  
  37. I've described this program, in its OSI Superboard-II incarnation, in
  38. comp.unix.wizards and news.sysadmin.  Now, here's the code for a UN*X
  39. version.  It seems to work OK on ncoast, except for some oddness in the
  40. "random" selections -- which are probably the result of our not having a
  41. good RNG.  (Could someone mail me one or more of the alternative
  42. generators?  Or maybe even the source to BSD random(), assuming that it
  43. falls outside the subset of BSD code that can be traced to AT&T?  To put it
  44. mildly, our random number generator isn't.)
  45.  
  46. This is not as complete as the original program, which actually had a list
  47. of characters (the current one uses phonemes) which were most likely to
  48. follow other phonemes.  On the other hand, this version does use phonemes
  49. rather than characters, so its creations are more pronounceable as a rule
  50. than those of the original.  The resulting passwords aren't quite as
  51. "natural" as the original, however.  (That may be for the best; the original
  52. was intended as a simple experiment to see if a rule for "proper English
  53. words" could be defined in terms of rules which related letters.  This was
  54. before I got to college and learned that such things were rather unlikely,
  55. but the original program still did a pretty good job of spewing out some
  56. common English words.  That can't be said for *this* program.)
  57.  
  58. To compile:    cc -O -o pwgen pwgen.c -lm
  59. (The sqrt() function is used in an attempt to produce a random number which
  60. is "weighted" toward one end of the range, so as to prefer one end of a list
  61. of "spellings" for a phoneme over the other end.  It seems to work, but with
  62. this rotted RNG, I can't be absolutely certain.  A trial distribution seems
  63. to be correct, however.)
  64.  
  65. What's the intent?  I find that I can remember a "word" better if I can
  66. pronounce it.  This may or may not be true for other people, but *anything*
  67. that encourages the use of random passwords is an improvement on what I
  68. typically see at client sites every day.  So this program spits out
  69. passwords which are virtually guaranteed not to be found in the dictionary,
  70. but are (usually) pronounceable to speakers of English.  (The tables can be
  71. modified for other languages, but they're rather hairy.  Perhaps I'll write
  72. a version which loads "compiled" language descriptions, and let the compiler
  73. deal with the hairy stuff.  Hey, they were even hairier in the BASIC
  74. version!)
  75.  
  76. Oh, well, shar and enjoy.
  77.  
  78. ++Brandon, sitting on the other side of the fence for the moment
  79. */
  80.  
  81. /*
  82.  * Generate (hopefully) pronounceable random passwords.  These can often be
  83.  * remembered more easily than completely random passwords, and are immune to
  84.  * dictionary searches, etc.
  85.  *
  86.  * The original version of this program was written in BASIC on an OSI
  87.  * Superboard II SBC.  That version is long gone (the SB2's cassette drive
  88.  * was never trustworthy, it eventually scrambled the tape), but the basic
  89.  * (pardon the pun) idea lives on here, with a few modification like basing
  90.  * the selection on "graphs" (actually, they are a restricted set of phonemes)
  91.  * and having randomly-selected spellings for those graphs.
  92.  */
  93.  
  94. #include <stdio.h>
  95. #include <math.h>
  96.  
  97. #define RANDOM(c)    ((int) (((random(c) & 0x7fff) / 32767.0) * (c)))
  98.  
  99. char *spelling[] = {
  100. /*a*/    "a",                (char *) 0,    /* 2*/
  101. /*A*/    "a",    "ae",    "ai",        (char *) 0,    /* 6*/
  102. /*b*/    "b",                (char *) 0,    /* 8*/
  103. /*ch*/    "ch",                (char *) 0,    /*10*/
  104. /*d*/    "d",                (char *) 0,    /*12*/
  105. /*e*/    "e",                (char *) 0,    /*14*/
  106. /*E*/    "e",    "ee",    "ie",        (char *) 0,    /*18*/
  107. /*f*/    "f",    "ph",    "gh",        (char *) 0,    /*22*/
  108. /*g*/    "g",                (char *) 0,    /*24*/
  109. /*h*/    "h",                (char *) 0,    /*26*/
  110. /*i*/    "i",    "e",            (char *) 0,    /*29*/
  111. /*I*/    "i",    "ai",            (char *) 0,    /*32*/
  112. /*i'*/    "i",    "ei",            (char *) 0,    /*35*/
  113. /*j*/    "j",    "g",            (char *) 0,    /*38*/
  114. /*k*/    "k",    "c",            (char *) 0,    /*41*/
  115. /*l*/    "l",                (char *) 0,    /*43*/
  116. /*m*/    "m",                (char *) 0,    /*45*/
  117. /*n*/    "n",                (char *) 0,    /*47*/
  118. /*ng*/    "ng",                (char *) 0,    /*49*/
  119. /*o*/    "o",    "a",    "ah",        (char *) 0,    /*53*/
  120. /*O*/    "o",    "oh",            (char *) 0,    /*56*/
  121. /*oo*/    "oo",    "u",            (char *) 0,    /*59*/
  122. /*OO*/    "oo",    "w",            (char *) 0,    /*62*/
  123. /*p*/    "p",                (char *) 0,    /*64*/
  124. /*qu*/    "qu",                (char *) 0,    /*66*/
  125. /*r*/    "r",                (char *) 0,    /*68*/
  126. /*s*/    "s",    "c",            (char *) 0,    /*71*/
  127. /*sh*/    "sh",    "s",            (char *) 0,    /*74*/
  128. /*t*/    "t",                (char *) 0,    /*76*/
  129. /*th*/    "th",                (char *) 0,    /*78*/
  130. /*TH*/    "th",                (char *) 0,    /*80*/
  131. /*u*/    "u",                (char *) 0,    /*82*/
  132. /*U*/    "u",    "oo",            (char *) 0,    /*85*/
  133. /*v*/    "v",                (char *) 0,    /*87*/
  134. /*x*/    "x",                (char *) 0,    /*89*/
  135. /*y*/    "y",                (char *) 0,    /*91*/
  136. /*z*/    "z",    "s",            (char *) 0,    /*94*/
  137. };
  138.  
  139. struct graph {
  140.     char *graph;
  141.     char type;
  142. #define CONSONANT    0
  143. #define VOWEL_LONG    1
  144. #define VOWEL_SHORT    2
  145. #define VOWEL_OTHER    3
  146. #define VOWEL_MASK    3
  147. #define iscons(c)    (((c)->type & VOWEL_MASK) == 0)
  148. #define isvowel(c)    (((c)->type & VOWEL_MASK) != 0)
  149. /*    char frequency;            */    /* unused for now */
  150.     char **spellings;
  151. /*    struct graph **following;    */    /* maybe later */
  152. } graph[] = {
  153.     "a",    VOWEL_SHORT,    &spelling[0],
  154.     "A",    VOWEL_LONG,    &spelling[2],
  155.     "b",    CONSONANT,    &spelling[6],
  156.     "ch",    CONSONANT,    &spelling[8],
  157.     "d",    CONSONANT,    &spelling[10],
  158.     "e",    VOWEL_SHORT,    &spelling[12],
  159.     "E",    VOWEL_LONG,    &spelling[14],
  160.     "f",    CONSONANT,    &spelling[18],
  161.     "g",    CONSONANT,    &spelling[22],
  162.     "h",    CONSONANT,    &spelling[24],
  163.     "i",    VOWEL_SHORT,    &spelling[26],
  164.     "I",    VOWEL_LONG,    &spelling[29],
  165.     "i'",    VOWEL_OTHER,    &spelling[32],
  166.     "j",    CONSONANT,    &spelling[35],
  167.     "k",    CONSONANT,    &spelling[38],
  168.     "l",    CONSONANT,    &spelling[41],
  169.     "m",    CONSONANT,    &spelling[43],
  170.     "n",    CONSONANT,    &spelling[45],
  171.     "ng",    CONSONANT,    &spelling[47],
  172.     "o",    VOWEL_SHORT,    &spelling[49],
  173.     "O",    VOWEL_LONG,    &spelling[53],
  174.     "oo",    VOWEL_SHORT,    &spelling[56],
  175.     "OO",    VOWEL_LONG,    &spelling[59],
  176.     "p",    CONSONANT,    &spelling[62],
  177.     "qu",    CONSONANT,    &spelling[64],
  178.     "r",    CONSONANT,    &spelling[66],
  179.     "s",    CONSONANT,    &spelling[68],
  180.     "sh",    CONSONANT,    &spelling[71],
  181.     "t",    CONSONANT,    &spelling[74],
  182.     "th",    CONSONANT,    &spelling[76],
  183.     "TH",    CONSONANT,    &spelling[78],
  184.     "u",    VOWEL_SHORT,    &spelling[80],
  185.     "U",    VOWEL_LONG,    &spelling[82],
  186.     "v",    CONSONANT,    &spelling[85],
  187.     "x",    CONSONANT,    &spelling[87],
  188.     "y",    CONSONANT,    &spelling[89],
  189.     "z",    CONSONANT,    &spelling[91],
  190.     0,    0,        &spelling[94],
  191. };
  192.  
  193. struct graph *vowel[] = {
  194.     &graph[0],    &graph[1],    &graph[5],    &graph[6],
  195.     &graph[10],    &graph[11],    &graph[12],    &graph[19],
  196.     &graph[20],    &graph[21],    &graph[22],    &graph[30],
  197.     &graph[31],
  198.     (struct graph *) 0,
  199. };
  200.  
  201. struct graph *consonant[] = {
  202.     &graph[2],    &graph[3],    &graph[4],    &graph[7],
  203.     &graph[8],    &graph[9],    &graph[13],    &graph[14],
  204.     &graph[15],    &graph[16],    &graph[17],    &graph[18],
  205.     &graph[23],    &graph[24],    &graph[25],    &graph[26],
  206.     &graph[27],    &graph[28],    &graph[29],    &graph[32],
  207.     &graph[33],    &graph[34],    &graph[35],
  208.     (struct graph *) 0,
  209. };
  210.  
  211. /*
  212.  * Randomly select a graph from the specifield array.  Eventually, this should
  213.  * account for graph frequencies as well.
  214.  */
  215.  
  216. struct graph *selgraph(graphs)
  217.     struct graph **graphs;
  218. {
  219.     register int cnt;
  220.  
  221.     for (cnt = 0; graphs[cnt] != (struct graph *) 0; cnt++)
  222.         ;
  223.     return graphs[RANDOM(cnt)];
  224. }
  225.  
  226. /*
  227.  * Randomly select a spelling for the specified graph.  This is not linear:
  228.  * earlier spellings are preferred over later ones, but the latter do
  229.  * sometimes sneak in.
  230.  */
  231.  
  232. char *selspell(graph)
  233.     struct graph *graph;
  234. {
  235.     register int cnt, sel;
  236.  
  237.     for (cnt = 0; graph->spellings[cnt] != (char *) 0; cnt++)
  238.         ;
  239.     if (cnt == 0) {
  240.         fprintf(stderr, "PANIC: selspell(%s) got count(spellings) == 0\n", graph->graph);
  241.         exit(2);
  242.     }
  243.     if (cnt == 1)
  244.         return *graph->spellings;
  245. /*
  246.  * This may not be the best way to do it... maybe Weemba'd care to lend a
  247.  * hand here?  After all, my specialty is programming, NOT math.
  248.  */
  249.     if ((sel = cnt - (int) sqrt((double) RANDOM(cnt * cnt) + 1) - 1) < 0 || sel >= cnt) {
  250. #ifdef BUGCATCH
  251.         fprintf(stderr, "PANIC: selspell(%s) got nlrand(%d) == %d\n", graph->graph, cnt, sel);
  252.         exit(2);
  253. #else
  254.         sel = 0;
  255. #endif
  256.     }
  257.     return graph->spellings[sel];
  258. }
  259.  
  260. /*
  261.  * Choose the next source for a graph.  The rules are:  a consonant MUST be
  262.  * followed by a vowel; a vowel may be followed by a vowel of a different
  263.  * type or by a consonant, but never more than two consecutive vowel graphs.
  264.  */
  265.  
  266. choosenext(cur, prev)
  267. {
  268.     if (cur == CONSONANT)
  269.         return VOWEL_MASK;
  270.     else if (prev == -1 || (prev & VOWEL_MASK) != 0)
  271.         return CONSONANT;
  272.     else if (RANDOM(10) == 5)
  273.         return VOWEL_MASK;
  274.     else
  275.         return CONSONANT;
  276. }
  277.  
  278. /*
  279.  * We are passed an array of (struct graph *); choose an entry randomly and
  280.  * assemble a string fitting the size constraint.  We use the original (OSI)
  281.  * paradigm:  alternate consonants and vowels, with the option of two vowels
  282.  * in a row occasionally.  The only difference is that they must be different
  283.  * *types* of vowels, a distinction that the OSI version didn't consider.
  284.  */
  285.  
  286. pwgen(initial, pw, maxlen)
  287.     struct graph **initial;
  288.     char *pw;
  289. {
  290.     int pwlen, state, prev, tmp;
  291.     struct graph *graph;
  292.     char *spelling;
  293.  
  294.     pwlen = 0;
  295.     state = initial[0]->type;
  296.     prev = -1;
  297.     while (pwlen < maxlen - 1) {
  298.         do {
  299.             graph = selgraph(initial);
  300.         } while (state != CONSONANT && graph->type == prev);
  301.         if ((spelling = selspell(graph)) == (char *) 0) {
  302.             fprintf(stderr, "PANIC: got NULL in selspell(%s)\n", graph->graph);
  303.             exit(2);
  304.         }
  305.         strcpy(pw, spelling);
  306.         while (*pw != '\0')
  307.             pwlen++, pw++;
  308.         tmp = prev;
  309.         prev = graph->type;
  310.         if ((state = choosenext(prev, tmp)) == CONSONANT)
  311.             initial = consonant;
  312.         else
  313.             initial = vowel;
  314.     }
  315. }
  316.  
  317. /*
  318.  * ==============================  End of pwgen  ==============================
  319.  */
  320.  
  321. #include <pwd.h>
  322. #include <ctype.h>
  323. #include <sys/types.h>
  324. #include <sys/stat.h>
  325.  
  326. #define TACPWFILE "./pwfile"
  327. #define TMPPWFILE "/tmp/tacpasswd.XXXXXX"
  328. #define PW_NAMLEN 8                /* max length user name */
  329. #define PW_PWDLEN 8                /* max length password  */
  330. #define GEN_PWDLEN 8                /* length of gen passwd */
  331. #define SPACE_PASSWD 20                /* room allocated for enc passwd */
  332. #define SPACE_DATE 20                /* room allocated for exp date */
  333. #define SHORTTIME 30
  334. #define LONGTIME 180
  335.  
  336. #define FSEEK_F_BEGIN 0
  337. #define FSEEK_F_CURR  1
  338. #define FSEEK_F_END   2
  339.  
  340. /*
  341.  * Externals
  342.  */
  343. extern char *optarg;
  344. extern int optind, opterr;
  345.  
  346. /*
  347.  * Global storage
  348.  */
  349. char *whoami;
  350. char *emptystring = "";
  351. char *usage = "Type '%s -h' for help\n";
  352. FILE *pwfile, *tmppwfile;
  353. int  generatepwd;
  354. int  expires_in;
  355.  
  356. /*
  357.  * Forward declarations
  358.  */
  359. char *mktemp(), *getpass(), *crypt();
  360. struct passwd *fgetpwent();
  361.  
  362. /*
  363.  * my_exit
  364.  *
  365.  * Close and remove the temporary file if necessary, and then exit.
  366.  */
  367. my_exit(code)
  368. int code;
  369. {
  370.     if (tmppwfile)
  371.     fclose(tmppwfile);
  372.     exit(code);
  373. }
  374.  
  375. /*
  376.  * create_entry
  377.  *
  378.  * Fill in a password structure with all information about a new TAC user.
  379.  */
  380. struct passwd *create_entry(name, fullname, lastuid)
  381.     char *name, *fullname;
  382.     int lastuid;
  383. {
  384.     struct passwd *newentry;
  385.  
  386.     fprintf(stderr, "%s: Creating entry for %0.*s\n", whoami, PW_NAMLEN, name);
  387.  
  388.     newentry = (struct passwd *)malloc(sizeof(struct passwd));
  389.     newentry->pw_name  = (char *)malloc(strlen(name)+1);
  390.     strncpy(newentry->pw_name, name, PW_NAMLEN);
  391.  
  392.     newentry->pw_passwd  = NULL;
  393.     newentry->pw_uid     = ++lastuid;
  394.     newentry->pw_gid     = lastuid;
  395. #ifdef PW_QUOTA
  396.     newentry->pw_quota   = 0;
  397. #endif
  398.     newentry->pw_comment = emptystring;
  399.  
  400.     if (fullname) {
  401.     newentry->pw_gecos = (char *)malloc(strlen(fullname)+1);
  402.     strcpy(newentry->pw_gecos, fullname);
  403.     } else
  404.     newentry->pw_gecos =  emptystring;
  405.  
  406.     newentry->pw_dir     = emptystring;
  407.     newentry->pw_shell   = NULL;
  408.     return(newentry);
  409. }
  410.  
  411. /*
  412.  * update_expiration
  413.  *
  414.  * Update the expiration date on this entry.  Passwords expire 180 days
  415.  * after they are initially set or changed.
  416.  */
  417. update_expiration(entry)
  418.     struct passwd *entry;
  419. {
  420.     long now;
  421.     char timestring[26];
  422.  
  423.     time(&now);
  424.     now += expires_in * 24 * 60 * 60;
  425.     strcpy(timestring, ctime(&now));
  426.  
  427.     if (entry->pw_shell)
  428.     free(entry->pw_shell);
  429.     entry->pw_shell = (char *)malloc(SPACE_DATE);
  430.     sprintf(entry->pw_shell, "%6.6s %4.4s, %d", timestring+4, timestring+20,
  431.         expires_in);
  432. }
  433.  
  434. /*
  435.  * make_passwd
  436.  *
  437.  * This routine sets up and calls the password generation routines.  It
  438.  * encrypts the result, and stores it in the password field of the
  439.  * structure.
  440.  */
  441. make_passwd(entry)
  442.     struct passwd *entry;
  443. {
  444.     char genpasswd[20], salt[2];
  445.  
  446.     pwgen((RANDOM(10) < 4? vowel: consonant), genpasswd, GEN_PWDLEN);
  447.  
  448.     salt[0] = '\0';
  449.     salt[1] = '\0';
  450.     while (!isalpha(salt[0]))
  451.       salt[0] = random() & 0x7f ;
  452.     while (!isalpha(salt[1]))
  453.       salt[1] = random() & 0x7f ;
  454.     if (entry->pw_passwd)
  455.     free(entry->pw_passwd);
  456.     entry->pw_passwd = (char *)malloc(SPACE_PASSWD);
  457.     strcpy(entry->pw_passwd, crypt(genpasswd, salt));
  458.     printf("New password for %s is \"%s\"\n", entry->pw_name, genpasswd);
  459. }
  460.  
  461. /*
  462.  * read_passwd
  463.  *
  464.  * This routine reads a new password from the terminal.
  465.  */
  466. read_passwd(entry)
  467.     struct passwd *entry;
  468. {
  469.     char *p, password[PW_PWDLEN], salt[2];
  470.  
  471.     strncpy(password, getpass("Password: "), PW_PWDLEN);
  472.     p = getpass("Again: ");
  473.     if (strncmp(password, p, PW_PWDLEN) != 0) {
  474.     fprintf(stderr, "Sorry\n");
  475.     my_exit(1);
  476.     }
  477.  
  478.     do {
  479.     salt[0] = random() & 0x7f ;
  480.     } while (!isalpha(salt[0]));
  481.     do {
  482.     salt[1] = random() & 0x7f ;
  483.     } while (!isalpha(salt[1]));
  484.       
  485.     if (entry->pw_passwd)
  486.     free(entry->pw_passwd);
  487.     entry->pw_passwd = (char *)malloc(SPACE_PASSWD);
  488.     strcpy(entry->pw_passwd, crypt(password, salt));
  489. }
  490.  
  491. /*
  492.  * copy_pwfile1
  493.  *
  494.  * Copy all entries in the password file before the one we are interested in.
  495.  */
  496. struct passwd *
  497. copy_pwfile1(name, lastuid)
  498.     char *name;
  499.     int *lastuid;
  500. {
  501.     struct passwd *current;
  502.  
  503.     fseek(pwfile, 0, FSEEK_F_BEGIN);
  504.     while (!feof(pwfile)) {
  505.     current = fgetpwent(pwfile);
  506.     if (current && strncmp(current->pw_name, name, PW_NAMLEN) != 0) {
  507.         *lastuid = current->pw_uid;
  508.         if (putpwent(current, tmppwfile) != 0) {
  509.         fprintf(stderr, "%s: Cannot put current entry for %0.*s\n",
  510.             whoami, PW_NAMLEN, current->pw_name);
  511.         perror(whoami);
  512.         my_exit(1);
  513.         }
  514.     } else {
  515.         return(current);
  516.     }
  517.     }
  518.     return(NULL);
  519. }
  520.  
  521. /*
  522.  * copy_pwfile2
  523.  *
  524.  * Add the new/modified entry.  Copy all entries in the password file after
  525.  * the one we are interested in.
  526.  */
  527. copy_pwfile2(new)
  528.     struct passwd *new;
  529. {
  530.     struct passwd *current;
  531.  
  532.     if (putpwent(new, tmppwfile) != 0) {
  533.     fprintf(stderr, "%s: Cannot put new entry for %0.*s\n",
  534.         whoami, PW_NAMLEN, new->pw_name);
  535.     perror(whoami);
  536.     my_exit(1);
  537.     }
  538.  
  539.     while (!feof(pwfile)) {
  540.     current = fgetpwent(pwfile);
  541.     if (current && putpwent(current, tmppwfile) != 0) {
  542.         fprintf(stderr, "%s: Cannot put current entry for %0.*s\n",
  543.             whoami, PW_NAMLEN, current->pw_name);
  544.         perror(whoami);
  545.         my_exit(1);
  546.     }
  547.     }
  548. }
  549.  
  550. /*
  551.  * copy_back
  552.  *
  553.  * Copy the new file back into the original file.  Can't use rename,
  554.  * because the files aren't guaranteed to be on the same file system.
  555.  */
  556. copy_back()
  557. {
  558.     char buffer[4096];
  559.     int count;
  560.  
  561.     fseek(pwfile, 0, FSEEK_F_BEGIN);
  562.     fseek(tmppwfile, 0, FSEEK_F_BEGIN);
  563.  
  564.     while ((count = fread(buffer, sizeof(char), 4096, tmppwfile)) > 0)
  565.       fwrite(buffer, sizeof(char), count, pwfile);
  566.  
  567.     fclose(pwfile);
  568.     fclose(tmppwfile);
  569. }
  570.  
  571. /*
  572.  * init
  573.  *
  574.  * Initialize the program.
  575.  */
  576. init(argc, argv, username, fullname)
  577.     int argc;
  578.     char **argv, **username, **fullname;
  579. {
  580.     char *c, *tmppwfilename, template[100];
  581.     int opt;
  582.  
  583.     whoami = argv[0];
  584.     generatepwd = 1;
  585.     expires_in = 180;
  586.  
  587.     while ((opt = getopt(argc, argv, "aghlst")) != EOF)
  588.     switch (opt) {
  589.     case 'a':
  590.         generatepwd = 0;
  591.         break;
  592.     case 'g':
  593.         generatepwd = 1;
  594.         break;
  595.     case 'h':
  596.         printf("\n%s: Add an entry to the TACACS password file.  The general\n", whoami);
  597.         printf("usage format for this command is:\n\n");
  598.         printf("    %s: [options] username fullname\n\n", whoami);
  599.         printf("The options available are:\n");
  600.         printf("    -a  Ask for a password.                  \n");
  601.         printf("    -g  Generate a password.        (default)\n");
  602.         printf("    -h  This message.                        \n");
  603.         printf("    -l  Long expiration - 180 days. (default)\n");
  604.         printf("    -s  Short expiration - 30 days.          \n");
  605.         printf("    -t  Temporary - 7 days.                  \n");
  606.         exit(1);
  607.     case 'l':
  608.         expires_in = 180;
  609.         break;
  610.     case 's':
  611.         expires_in = 30;
  612.         break;
  613.     case 't':
  614.         expires_in = 7;
  615.         break;
  616.     default:
  617.         fprintf(stderr, usage, whoami);
  618.         exit(1);
  619.     }
  620.     if ((argc - optind < 1) || (argc - optind > 2)) {
  621.     fprintf(stderr, usage, whoami);
  622.     exit(1);
  623.     }
  624.  
  625.     *username = argv[optind++];
  626.     if (strlen(username) > 8)
  627.     username[8] = '\0';
  628.     *fullname = argv[optind] ? argv[optind] : NULL;
  629.  
  630.     for (c = *username; *c; c++)
  631.       if (!isalnum(*c)) {
  632.       fprintf(stderr, "%s: bad character (%c) in username\n", whoami, *c);
  633.       exit(1);
  634.       }
  635.     if (*fullname)
  636.       for (c = *fullname; *c; c++)
  637.     if ((!isprint(*c)) || (*c == ':')) {
  638.         fprintf(stderr, "%s: bad character (%c) in fullname\n", whoami, *c);
  639.         exit(1);
  640.     }
  641.     
  642.     /*
  643.      * Open TAC password file, and scratch file
  644.      */
  645.     if ((pwfile = fopen(TACPWFILE, "a+")) == NULL) {
  646.     fprintf(stderr, "%s: cannot open TACACS password file\n", whoami);
  647.     perror(whoami);
  648.     my_exit(1);
  649.     }
  650.     strcpy(template, TMPPWFILE);
  651.     tmppwfilename = mktemp(template);
  652.     if ((tmppwfile = fopen(tmppwfilename, "w+")) == NULL) {
  653.     fprintf(stderr, "%s: cannot open temporary file %s\n", whoami, tmppwfilename);
  654.     perror(whoami);
  655.     my_exit(1);
  656.     }
  657.     if (unlink(tmppwfilename) != 0) {
  658.     fprintf(stderr, "%s: cannot unlink temporary file %s\n", whoami, tmppwfilename);
  659.     perror(whoami);
  660.     my_exit(1);
  661.     }
  662.  
  663.     srandom(time(0) + (getpgrp() << 8) + getpid());
  664. }
  665.  
  666. main(argc, argv)
  667.     int argc;
  668.     char **argv;
  669. {
  670.     struct passwd *entry;
  671.     int lastuid = 0;
  672.     char *username, *fullname;
  673.  
  674.     init(argc, argv, &username, &fullname);
  675.     entry = copy_pwfile1(username, &lastuid);
  676.     if (entry == NULL)
  677.       entry = create_entry(username, fullname, lastuid);
  678.     update_expiration(entry);
  679.     if (generatepwd)
  680.       make_passwd(entry);
  681.     else
  682.       read_passwd(entry);
  683.     copy_pwfile2(entry);
  684.     copy_back();
  685.     my_exit(0);
  686. }
  687.